﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	The GNU Library General Public License (LGPL) used in RapidWebDev is 
	intended to guarantee your freedom to share and change free software - to 
	make sure the software is free for all its users.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Library General Public License (LGPL) for more details.

	You should have received a copy of the GNU Library General Public License (LGPL)
	along with this program.  
	If not, see http://www.rapidwebdev.org/Content/ByUniqueKey/OpenSourceLicense
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace RapidWebDev.Common.WikiContent
{
	/// <summary>
	/// Api to replace wiki markup with the display web page HTML.
	/// </summary>
	public static class WikiMarkupApi
	{
		private static readonly WikiPlex.WikiEngine wikiEngine = new WikiPlex.WikiEngine();
		private static readonly IContentRenderingConfigApi contentRenderingConfigApi = SpringContext.Current.GetObject<IContentRenderingConfigApi>();

		/// <summary>
		/// Replace wiki text with the displaying web page HTML.
		/// </summary>
		/// <param name="wiki"></param>
		/// <returns></returns>
		public static string Replace(string wiki)
		{
			return wikiEngine.Render(wiki);
		}

		/// <summary>
		/// Replace wiki text with the displaying web page HTML.
		/// </summary>
		/// <param name="wiki"></param>
		/// <param name="option"></param>
		/// <returns></returns>
		public static string Replace(string wiki, WikiMarkupReplaceOption option)
		{
			if (string.IsNullOrEmpty(wiki)) return wiki;
			string htmlOutput = wikiEngine.Render(wiki);
			if (string.IsNullOrEmpty(htmlOutput)) return htmlOutput;

			IList<SpotlightKeyword> spotlightKeywords = ResolveSpotlightKeywords(option);
			int linkingKeywordNumber = 0;
			foreach (SpotlightKeyword spotlightKeyword in spotlightKeywords)
			{
				Regex regex = CreateRegex(spotlightKeyword.Keyword);
				if (regex == null) continue;

				int matchingStartIndex = 0;
				for (int i = 0; i < spotlightKeyword.ReplacementNumber; i++)
				{
					Match match = regex.Match(htmlOutput, matchingStartIndex);
					if (match == null || !match.Success) break;

					string matchingKeyword = match.Value.Substring(1, match.Value.Length - 2);
					string linkingKeyword = CreateLinkingKeyword(matchingKeyword, spotlightKeyword);

					string textBeforeLinkingKeyword = htmlOutput.Substring(0, match.Index + 1);
					matchingStartIndex = textBeforeLinkingKeyword.Length + linkingKeyword.Length;

					string newHtmlOutput = textBeforeLinkingKeyword + linkingKeyword;
					if (match.Index + match.Length - 1 < htmlOutput.Length)
						newHtmlOutput += htmlOutput.Substring(match.Index + match.Length - 1);

					htmlOutput = newHtmlOutput;
					linkingKeywordNumber++;
					if (linkingKeywordNumber >= option.MaxLinkingKeywords)
						return htmlOutput;
				}
			}

			return htmlOutput;
		}

		private static IList<SpotlightKeyword> ResolveSpotlightKeywords(WikiMarkupReplaceOption option)
		{
			List<SpotlightKeyword> spotlightKeywords = new List<SpotlightKeyword>();
			HashSet<string> keywordHashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (option.SpotlightKeywords != null && option.SpotlightKeywords.Count > 0)
			{
				foreach (SpotlightKeyword spotlightKeyword in option.SpotlightKeywords)
				{
					if (keywordHashSet.Contains(spotlightKeyword.Keyword)) continue;
					spotlightKeywords.Add(spotlightKeyword);
					keywordHashSet.Add(spotlightKeyword.Keyword);
				}
			}

			if (!string.IsNullOrEmpty(option.ContentRenderingConfigId))
			{
				ContentRenderingConfig contentRenderingConfig = contentRenderingConfigApi.Resolve(option.ContentRenderingConfigId);
				if (contentRenderingConfig == null)
					throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The content rendering configuration id \"{0}\" does not exist.", option.ContentRenderingConfigId));

				foreach (SpotlightKeyword spotlightKeyword in contentRenderingConfig.SpotlightKeyword)
				{
					if (keywordHashSet.Contains(spotlightKeyword.Keyword)) continue;
					spotlightKeywords.Add(spotlightKeyword);
					keywordHashSet.Add(spotlightKeyword.Keyword);
				}
			}

			return spotlightKeywords;
		}

		private static string CreateLinkingKeyword(string keyword, SpotlightKeyword spotlightKeyword)
		{
			StringBuilder output = new StringBuilder();
			output.AppendFormat("<a href=\"{0}\"", spotlightKeyword.TargetUrl);
			if(spotlightKeyword.TargetType == TargetType.Blank)
				output.Append(" target=\"_blank\"");
			if (spotlightKeyword.NoFollow)
				output.Append(" rel=\"nofollow\"");
			output.Append(">");
			output.Append(keyword);
			output.Append("</a>");
			return output.ToString();
		}

		private static Regex CreateRegex(string keyword)
		{
			if (string.IsNullOrEmpty(keyword)) return null;
			string regexExp = @"[^>a-zA-Z0-9_]+" + keyword.Replace(" ", "[ ]") + @"[^<a-zA-Z0-9_]+";
			return new Regex(regexExp, RegexOptions.Compiled | RegexOptions.IgnoreCase);
		}
	}

	/// <summary>
	/// The option to replace wiki markup of the text.
	/// </summary>
	public class WikiMarkupReplaceOption
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public WikiMarkupReplaceOption()
		{
			this.MaxLinkingKeywords = int.MaxValue;
		}

		/// <summary>
		/// Max linking keywords, defaults to Unlimited.
		/// </summary>
		public int MaxLinkingKeywords { get; set; }

		/// <summary>
		/// Id of the content rendering configuration, defaults to NULL.
		/// </summary>
		public string ContentRenderingConfigId { get; set; }

		/// <summary>
		/// The list of spotlight keywords, defaults to NULL.
		/// </summary>
		public IList<SpotlightKeyword> SpotlightKeywords { get; set; }
	}
}
